﻿using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using QuickTest.Core.Compilation;

namespace QuickTest.Services.TextRetrieval
{
    /// <summary>
    /// Retrieves text from an embedded resource
    /// </summary>
    public class EmbeddedResourceTextRetriever : ITextRetriever
    {
        #region Construction

        public EmbeddedResourceTextRetriever(EmbeddedResourceTextRetrieverSettings settings)
        {
            if (settings == EmbeddedResourceTextRetrieverSettings.Empty)
                throw new ArgumentException("TextRetriever settings cannot be empty");

            Settings = settings;
        }

        #endregion

        #region Fields

        private string cachedText = string.Empty;

        #endregion

        #region Properties

        private EmbeddedResourceTextRetrieverSettings Settings { get; set; }

        #endregion

        #region Methods

        /// <summary>
        /// Returns the text from the embedded resource provided by the retriever's settings. 
        /// Will cache the text and not call out to the resource on subsequent invocations
        /// </summary>
        /// <returns></returns>
        public string GetText()
        {
            if (string.IsNullOrEmpty(cachedText))
            {
                using (TextReader reader = new StreamReader(GetResourceStream()))
                {
                    cachedText = reader.ReadToEnd();
                }
            }

            return cachedText;
        }

        protected virtual Stream GetResourceStream()
        {
            Stream resourceStream = Assembly.Load(Settings.AssemblyName).GetManifestResourceStream(Settings.ResourceName);

            if (resourceStream == null)
            {
                throw new ServiceException("Resource not found in given assembly");
            }

            return resourceStream;
        }

        #endregion
    }

    /// <summary>
    /// Settings for an EmbeddedResourceTextRetriever
    /// </summary>
    [StructLayout(LayoutKind.Auto)]
    public struct EmbeddedResourceTextRetrieverSettings : IEquatable<EmbeddedResourceTextRetrieverSettings>
    {
        #region Static

        /// <summary>
        /// Represents an empty set of settings
        /// </summary>
        public static EmbeddedResourceTextRetrieverSettings Empty;

        private static string[] ParseAssemblyQualifedResourceName(string name)
        {
            string[] parseResults = name.Split(',');

            if (parseResults.Length != 2)
                throw new ArgumentException("ResourceName is not assembly qualified");

            return parseResults;
        }

        #endregion

        #region Construction

        public EmbeddedResourceTextRetrieverSettings(string assemblyQualifedResourceName)
        {
            if (string.IsNullOrEmpty(assemblyQualifedResourceName))
                throw new ArgumentException("AssemblyQualifedResourceName is required");

            string[] resourceDetails = ParseAssemblyQualifedResourceName(assemblyQualifedResourceName);

            assemblyName = resourceDetails[0].Trim();
            resourceName = resourceDetails[1].Trim();
        }

        #endregion

        #region Field

        private readonly string assemblyName;
        private readonly string resourceName;

        #endregion

        #region Properties

        /// <summary>
        /// The assembly name where the resource resides
        /// </summary>
        internal string AssemblyName
        {
            get { return assemblyName; }
        }

        /// <summary>
        /// The name of the resource file
        /// </summary>
        internal string ResourceName
        {
            get { return resourceName; }
        }

        #endregion

        #region Equality

        public override int GetHashCode()
        {
            return assemblyName.GetHashCode() + resourceName.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;

            if (! (obj is EmbeddedResourceTextRetrieverSettings))
                return false;

            return Equals((EmbeddedResourceTextRetrieverSettings) obj);
        }

        public bool Equals(EmbeddedResourceTextRetrieverSettings settings)
        {
            return AssemblyName == settings.AssemblyName && ResourceName == settings.ResourceName;
        }

        public static bool operator ==(EmbeddedResourceTextRetrieverSettings settings1, EmbeddedResourceTextRetrieverSettings settings2)
        {
            return settings1.Equals(settings2);
        }

        public static bool operator !=(EmbeddedResourceTextRetrieverSettings settings1, EmbeddedResourceTextRetrieverSettings settings2)
        {
            return ! settings1.Equals(settings2);
        }

        #endregion
    }
}